
;--- helper thread for multimedia (timer, waveout, midiout)

	.386
if ?FLAT
	.MODEL FLAT, stdcall
else
	.MODEL SMALL, stdcall
endif
	option casemap:none
	option proc:private

	include winbase.inc
	include winuser.inc
	include mmsystem.inc
	include winmm.inc
	include macros.inc

_waveDequeueHdr proto
_midithreadproc proto
_timethreadproc proto :DWORD

externdef g_hWOEvent:DWORD
externdef g_hMOTimer:DWORD

	.data

g_hMMThread		dd 0
g_hEvent        dd 0
g_bRefCnt		dd 0
g_bCancel		db 0

	.CODE

;--- the mm thread is created if there is a notification
;--- to be done (which can't be made from the sound IRQ procedure)
;--- or if midi sound has to be feeded

mmthreadproc proc lParam:DWORD

		@strace <"mm helper thread starting">
		.while (!g_bCancel)
ifdef _DEBUG
			invoke GetCurrentThread
endif
			@strace <"mmthreadproc: still alive, [thread=", eax, ",", dword ptr [eax+4], "]">
			invoke EnterCriticalSection, addr g_csMM
			xor esi, esi
			mov eax, g_pTimer
			.while (eax)
				push [eax].TIMEOBJ.hTimer
				inc esi
				mov eax, [eax].TIMEOBJ.pNext
			.endw
			mov edi, esi		;number of timers in edi
			.if (g_hWOEvent)
				@strace <"mmthreadproc: calling _waveDequeueHdr">
				invoke _waveDequeueHdr
				push g_hWOEvent
				inc esi
			.endif
ife ?USEMMTIMER
			.if (g_hMOTimer)
				@strace <"mmthreadproc: calling _midithreadproc">
				invoke _midithreadproc
				.if (g_hMOTimer)
					push g_hMOTimer
					inc esi
				.endif
			.endif
endif
			invoke LeaveCriticalSection, addr g_csMM
			push g_hEvent
			inc esi
			.if (esi == 1)
				mov edx, [esp+0]
				invoke WaitForSingleObject, edx, INFINITE
			.else 
				mov edx, esp
				invoke WaitForMultipleObjects, esi, edx, 0, INFINITE
			.endif
ifdef _DEBUG
			.if (eax == WAIT_FAILED)
				@strace <"mmthreadproc: wait function failed!!!">
			.endif
endif
			mov ecx, esi			;total objects -> ecx
			sub ecx, edi			;non-timer objects -> ecx
			sub eax, WAIT_OBJECT_0
			.if ((eax >= ecx) && (eax < esi))
				mov eax, [esp+eax*4]
				@strace <"mmthreadproc: calling _timethreadproc, eax=", eax, " esi=", esi, " edi=", edi, " ecx=", ecx>
				invoke _timethreadproc, eax
ifdef _DEBUG
			.else
				@strace <"mmthreadproc: wait returned, eax=", eax, " esi=", esi>
;				invoke ResetEvent, g_hEvent	;not needed
endif
			.endif
			lea esp, [esp+esi*4]
		.endw
if 0
		xor eax, eax
		xchg eax, g_hWOEvent
		.if (eax)
			invoke CloseHandle, eax
		.endif
  ife ?USEMMTIMER
		xor eax, eax
		xchg eax, g_hMOTimer
		.if (eax)
			push eax
			invoke CancelWaitableTimer, eax
			pop eax
			invoke CloseHandle, eax
		.endif
  endif
endif
		@strace <"mm helper thread exiting">
		ret
		align 4
mmthreadproc endp

;--- termination.
;--- since dll termination code is serialized,
;--- it's not possible to run the mm thread here!

stopthread proc
		@strace <"stop mm thread: enter">
		.if (g_hMMThread)
;--- cant wait in dll termination code
if 0
			mov g_bCancel, 1
			invoke SetEvent, g_hEvent
			invoke WaitForSingleObject, g_hMMThread, 200
else
			invoke TerminateThread, g_hMMThread, 0	;this suspends the thread
endif
			xor ecx, ecx
			xchg ecx, g_hMMThread
			invoke CloseHandle, ecx
			xor ecx, ecx
			xchg ecx, g_hEvent
			invoke CloseHandle, ecx
		.endif
		xor ecx, ecx
		xchg ecx, g_hWOEvent
		jecxz @F
		invoke CloseHandle, ecx
@@:
ife ?USEMMTIMER
		xor ecx, ecx
		xchg ecx, g_hMOTimer
		jecxz @F
		push ecx
		invoke CancelWaitableTimer, ecx
		pop eax
		invoke CloseHandle, eax
@@:
endif
		@strace <"stop mm thread: exit">
		ret
		align 4
stopthread endp

StartMMThread proc public

		invoke EnterCriticalSection, addr g_csMM
		.if (!g_hMMThread)
			invoke CreateEvent, 0, FALSE, 0, 0
			and eax, eax
			jz exit
			mov g_hEvent, eax
			push 0
			invoke CreateThread, 0, 0, offset mmthreadproc, 0, CREATE_SUSPENDED, esp
			pop edx
			.if (eax)
				mov g_hMMThread, eax
				invoke SetThreadPriority, eax, THREAD_PRIORITY_TIME_CRITICAL
				invoke atexit, offset stopthread
			.else
				invoke CloseHandle, g_hEvent
				mov g_hEvent, 0
				jmp exit
			.endif
		.endif
		inc g_bRefCnt
		.if (g_bRefCnt == 1)
			@strace <"StartMMThread: calling ResumeThread(", g_hMMThread, ")">
			invoke ResumeThread, g_hMMThread
		.endif
		@strace <"StartMMThread: calling SetEvent">
		invoke SetEvent, g_hEvent
exit:
		invoke LeaveCriticalSection, addr g_csMM
		mov eax, g_hMMThread
		@strace <"StartMMThread()=", eax, " cnt=", g_bRefCnt>
		ret
		align 4
StartMMThread endp

StopMMThread proc public

		@strace <"StopMMThread() enter, cnt=", g_bRefCnt>
		.if (g_hMMThread)
			invoke GetCurrentThread
			dec g_bRefCnt
			.if (ZERO?)
				cmp eax,g_hMMThread
				jz done
				@strace <"StopMMThread: calling SuspendThread(", g_hMMThread, ")">
				invoke SuspendThread, g_hMMThread
			.else
				cmp eax,g_hMMThread
				jz done
				@strace <"StopMMThread: calling SetEvent()">
				invoke SetEvent, g_hEvent
ifdef _DEBUG
				invoke GetCurrentThread
				@strace <"StopMMThread: calling SwitchToThread() [thread=", eax, "]">
endif
				invoke SwitchToThread	;give the helper thread a time slice
			.endif
		.endif
done:
		@strace <"StopMMThread() exit, cnt=", g_bRefCnt>
		ret
		align 4
StopMMThread endp

	end
